package lv.rtu.ditf.dc.fs;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import lv.rtu.ditf.dc.hashmap.routines.HashMapRoutines;
import lv.rtu.ditf.dc.stream.StringStreamWriter;

/**
 * 
 * @author Pāvels Koliškins, Edgars Matisāns
 *
 */
public class DocumentCategory implements DocumentCategoryOperationsSource {
	private static final String DEFAULT_EXTENSION = ".txt";
	
	private final File categoryDirectory;
	private final File indexesDirectory;
	private List<Document> documentsList;
	
	private Set<DocumentCategoryOperationsListener> listeners;

    private DocumentCategory(File categoryDirectory) {
		this.categoryDirectory = categoryDirectory;
		this.indexesDirectory = null;
	}

	private DocumentCategory(File categoryDirectory, File indexesDirectory) {
		this.categoryDirectory = categoryDirectory;
		this.indexesDirectory = indexesDirectory;
	}
	
	/**
	 * 
	 * @param categoryDirectory
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static DocumentCategory get(File categoryDirectory, File baseIndexesDirectory) throws IOException {
		if (categoryDirectory == null || !categoryDirectory.exists())
			throw new IllegalArgumentException("Document category must exist");
		
		// ignore files
		if (categoryDirectory.isFile())
			return null;
		
		File indexesDirectory = new File(baseIndexesDirectory, categoryDirectory.getName());
		if (!indexesDirectory.exists())
			FileStorage.checkDirectory(indexesDirectory);
		
		return new DocumentCategory(categoryDirectory, indexesDirectory);
	}

    public static DocumentCategory get(File categoryDirectory) throws IOException {
		if (categoryDirectory == null || !categoryDirectory.exists())
			throw new IllegalArgumentException("Document category must exist");

		// ignore files
		if (categoryDirectory.isFile())
			return null;

		return new DocumentCategory(categoryDirectory);
	}
	
	public String getName() {
		return categoryDirectory.getName();
	}
	
	/**
	 * Gets list of all documents from this category. 
	 * @return list of category documents. IF category doesn't contain any document
	 * 	empty list is returned.
	 */
	public List<Document> getDocuments() {
		if (documentsList == null) {
			documentsList = new ArrayList<Document>();
			
			File[] files = categoryDirectory.listFiles();
			if (files == null || files.length == 0)
				return documentsList;
			
			for (File documentFile : files) {
	            if (!documentFile.isHidden()) {
	                Document document = Document.get(documentFile, indexesDirectory);
	                if (document != null)
	                    documentsList.add(document);
	            }
			}
		}
		
		return documentsList;
	}
	
	public HashMap<String, Integer> getDocumentTermFrequencies() {
		List<Document> documents = getDocuments();
		
		HashMap<String, Integer> resultMap = new HashMap<String, Integer>();
		for (Document document : documents)
			HashMapRoutines.mergeAndSum(resultMap, document.getPrecalculatedTermFrequencies());
		
		return resultMap;
	}
	
	public HashMap<String, Integer> getDocumentCountByTerm() {
		List<Document> documents = getDocuments();
		
		HashMap<String, Integer> resultMap = new HashMap<String, Integer>();
		for (Document document : documents)
			HashMapRoutines.mergeAndCount(resultMap, document.getPrecalculatedTermFrequencies());
		
		return resultMap;
	}
	
	public void addDocument(String documentContent) {
		File documentFile = new File(categoryDirectory, createFileName());
		FileOutputStream fis = null;
		try {
			if (documentFile.createNewFile()) {
				fis = new FileOutputStream(documentFile);
				StringStreamWriter.write(documentContent, fis, null);
				
				if (documentsList != null) {
					Document document = Document.get(documentFile, indexesDirectory);
					documentsList.add(document);
					documentAdded(document);
				}
			}
		}
		catch (IOException ex) {
			//TODO log
			ex.printStackTrace();
		}
		finally {
			if (fis != null)
				try {
					fis.close();
				} catch (IOException e) {
					// TODO log
					e.printStackTrace();
				}
		}
	}

	public void buildIndexes() {
		// delete old indexes
		FileStorage.deleteDirectoryContents(indexesDirectory);
		
		List<Document> documents = getDocuments();
		for (Document document : documents)
			document.buildIndex();
	}
	
	private String createFileName() {
		return System.currentTimeMillis() + "_" + Thread.currentThread().getId() + DEFAULT_EXTENSION;
	}

	@Override
	public void addDocumentCategoryOperationsListener(DocumentCategoryOperationsListener listener) {
		if (listeners == null)
			listeners = new HashSet<DocumentCategoryOperationsListener>();
		
		listeners.add(listener);
	}

	@Override
	public void removeDocumentCategoryOperationsListener(DocumentCategoryOperationsListener listener) {
		if (listeners != null)
			listeners.remove(listener);
	}
	
	private void documentAdded(Document document) {
		if (listeners != null) {
			DocumentCategoryOperationEvent event = new DocumentCategoryOperationEvent(this, document);
			
			for (DocumentCategoryOperationsListener listener : listeners)
				listener.onDocumentAdded(event);
		}
	}
}